package org.wzy.springmvc.framework.context;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.wzy.springmvc.framework.annotation.MAutowired;
import org.wzy.springmvc.framework.annotation.MController;
import org.wzy.springmvc.framework.annotation.MService;

public class MApplicationContext {

	private List<String> classCache = new ArrayList<>();

	private Map<String, Object> instanceMap = new ConcurrentHashMap<String, Object>();

	private Properties properties = new Properties();
	
	public MApplicationContext(String location) {
		// 加载properties文件(即配置文件)
		// 定位、加载、注册、初始化、注入

		InputStream input = null;
		try {
			// 定位
			input = this.getClass().getClassLoader().getResourceAsStream(location);

			// 加载
			properties.load(input);

			// 注册,把所有class找出来
			String packagesToScan = properties.getProperty("scanPackage");
			doRegister(packagesToScan);

			// 实例化需要ioc的对象(加了注解的对象),只需要要循环class
			doCreateBean();

			// 注入
			populate();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 把符合条件的所有的class全部找出来，注册到缓存中
	 * 
	 * @param packagesToScan
	 */
	private void doRegister(String packagesToScan) {
		URL url = this.getClass().getClassLoader().getResource("/" + packagesToScan.replaceAll("\\.", "/"));
		File dir = new File(url.getFile());
		for (File file : dir.listFiles()) {
			// 如果是一个文件夹，就继续递归查找
			if (file.isDirectory()) {
				doRegister(packagesToScan + "." + file.getName());
			} else {
				classCache.add(packagesToScan + "." + file.getName().replace(".class", "").trim());
			}
		}
	}

	private void doCreateBean() {
		// 检查看有没有注册信息，注册心里里保存了所有的class名称
		// BeanDefinition 保存了类的名称，也保存了类和类之间的关系(Map/List/Set/ref/parent)了
		if (classCache.size() == 0)
			return;

		try {
			for (String className : classCache) {
				
				// 直接反射出来，不再作代理(这里spring使用了判断，用的是代理)
				Class<?> clazz = Class.forName(className);

				// 哪个类需要初始化
				// 只要加了类似于@Service/@Controller这样的注解都需要初始化
				if (clazz.isAnnotationPresent(MController.class)) {
					// 名字默认：类名首字母小写
					instanceMap.put(lowerFirstChar(clazz.getSimpleName()), clazz.newInstance());
				} else if (clazz.isAnnotationPresent(MService.class)) {
					MService service = clazz.getAnnotation(MService.class);

					// 如果设置了自定义名字，就优先使用自定义名称
					String id = service.value();
					if (!"".equals(id.trim())) {
						instanceMap.put(id, clazz.newInstance());
						continue;
					}

					// 如果是空的，就使用默认规则
					// 1. 类名首字母小写
					// 如果类是接口？
					// 2. 根据类型匹配
					Class<?>[] interfaces = clazz.getInterfaces();
					// 如果这个类实现了接口，就使用接口的类型作为id
					for (Class<?> clz : interfaces) {
						instanceMap.put(clz.getName(), clazz.newInstance());
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void populate() {
		// 首先要判断ioc容器中有没有东西
		if (instanceMap.isEmpty()) {
			return;
		}

		for (Entry<String, Object> entry : instanceMap.entrySet()) {
			// 把所有的属性全部取出来，包括私有属性
			Field[] fields = entry.getValue().getClass().getDeclaredFields();
			for (Field field : fields) {
				if (!field.isAnnotationPresent(MAutowired.class)) {
					continue;
				}

				MAutowired autowired = (MAutowired) field.getAnnotation(MAutowired.class);
				String value = autowired.value().trim();
				// 如果value为空，也就是说自己没有设置，默认根据类型来注入
				if ("".equals(value)) {
					value = field.getType().getName();
				} else {

				}
				field.setAccessible(true);

				try {
					field.set(entry.getValue(), instanceMap.get(value));
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
					continue;
				} catch (IllegalAccessException e) {
					e.printStackTrace();
					continue;
				}
			}
		}
	}
	
	public Object getBean(String name) {
		return null;
	}

	public Map<String, Object> getAll() {
		return instanceMap;
	}

	private static String lowerFirstChar(String str) {
		char[] charArray = str.toCharArray();
		charArray[0] += 32;

		return String.valueOf(charArray);
	}

	public Properties getProperties() {
		return properties;
	}
}
